/*
 * Copyright 2002-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.beans.factory.support;

import java.beans.PropertyEditor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.PropertyEditorRegistrySupport;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanIsAbstractException;
import org.springframework.beans.factory.BeanIsNotAFactoryException;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.CannotLoadBeanClassException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.config.Scope;
import org.springframework.core.CollectionFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 * 实现{@link org.springframework.beans.factory.BeanFactory}的抽象基本类，提供了
 * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}
 * SPI中所有的功能。并不假定为一个listable bean
 * factory，也可以作为从后端资源（访问bean定义是一个很昂贵的行为）获取Bean定义的Bean factory的基类实现，<br/>
 * 
 * Abstract base class for {@link org.springframework.beans.factory.BeanFactory}
 * implementations, providing the full capabilities of the
 * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory} SPI.
 * Does <i>not</i> assume a listable bean factory: can therefore also be used as
 * base class for bean factory implementations which obtain bean definitions
 * from some backend resource (where bean definition access is an expensive
 * operation).
 * 
 * <p>
 * This class provides a singleton cache (through its base class
 * {@link org.springframework.beans.factory.support.DefaultSingletonBeanRegistry}
 * , singleton/prototype determination,
 * {@link org.springframework.beans.factory.FactoryBean} handling, aliases, bean
 * definition merging for child bean definitions, and bean destruction (
 * {@link org.springframework.beans.factory.DisposableBean} interface, custom
 * destroy methods). Furthermore, it can manage a bean factory hierarchy
 * (delegating to the parent in case of an unknown bean), through implementing
 * the {@link org.springframework.beans.factory.HierarchicalBeanFactory}
 * interface.
 * 
 * <p>
 * The main template methods to be implemented by subclasses are
 * {@link #getBeanDefinition} and {@link #createBean}, retrieving a bean
 * definition for a given bean name and creating a bean instance for a given
 * bean definition, respectively. Default implementations of those operations
 * can be found in {@link DefaultListableBeanFactory} and
 * {@link AbstractAutowireCapableBeanFactory}.
 * 
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 15 April 2001
 * @see #getBeanDefinition
 * @see #createBean
 * @see AbstractAutowireCapableBeanFactory#createBean
 * @see DefaultListableBeanFactory#getBeanDefinition
 */
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry
		implements ConfigurableBeanFactory {

	/** Parent bean factory, for bean inheritance support */
	private BeanFactory parentBeanFactory;

	/** ClassLoader to resolve bean class names with, if necessary */
	private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();

	/** Whether to cache bean metadata or rather reobtain it for every access */
	private boolean cacheBeanMetadata = true;

	/** Custom PropertyEditors to apply to the beans of this factory */
	private final Map customEditors = new HashMap();

	/** Custom PropertyEditorRegistrars to apply to the beans of this factory */
	private final Set propertyEditorRegistrars = CollectionFactory
			.createLinkedSetIfPossible(16);

	/** BeanPostProcessors to apply in createBean */
	private final List beanPostProcessors = new ArrayList();

	/**
	 * Indicates whether any InstantiationAwareBeanPostProcessors have been
	 * registered
	 */
	private boolean hasInstantiationAwareBeanPostProcessors;

	/**
	 * Indicates whether any DestructionAwareBeanPostProcessors have been
	 * registered
	 */
	private boolean hasDestructionAwareBeanPostProcessors;

	/** Map from scope identifier String to corresponding Scope */
	private final Map scopes = new HashMap();

	/** Map from alias to canonical bean name */
	private final Map aliasMap = CollectionFactory
			.createConcurrentMapIfPossible(16);

	/** Map from bean name to merged RootBeanDefinition */
	private final Map mergedBeanDefinitions = CollectionFactory
			.createConcurrentMapIfPossible(16);

	/** Names of beans that have already been created at least once */
	private final Set alreadyCreated = Collections
			.synchronizedSet(new HashSet());

	/** Names of beans that are currently in creation */
	private final ThreadLocal prototypesCurrentlyInCreation = new ThreadLocal();

	/**
	 * Cache of singleton objects created by FactoryBeans: FactoryBean name -->
	 * object
	 */
	private final Map factoryBeanObjectCache = new HashMap();

	/**
	 * Create a new AbstractBeanFactory.
	 */
	public AbstractBeanFactory() {
	}

	/**
	 * Create a new AbstractBeanFactory with the given parent.
	 * 
	 * @param parentBeanFactory
	 *            parent bean factory, or <code>null</code> if none
	 * @see #getBean
	 */
	public AbstractBeanFactory(BeanFactory parentBeanFactory) {
		this.parentBeanFactory = parentBeanFactory;
	}

	// ---------------------------------------------------------------------
	// Implementation of BeanFactory interface
	// ---------------------------------------------------------------------

	public Object getBean(String name) throws BeansException {
		return getBean(name, null, null);
	}

	public Object getBean(String name, Class requiredType)
			throws BeansException {
		return getBean(name, requiredType, null);
	}

	/**
	 * Return an instance, which may be shared or independent, of the specified
	 * bean.
	 * 
	 * @param name
	 *            the name of the bean to retrieve
	 * @param args
	 *            arguments to use if creating a prototype using explicit
	 *            arguments to a static factory method. It is invalid to use a
	 *            non-null args value in any other case.
	 * @return an instance of the bean
	 * @throws BeansException
	 *             if the bean could not be created
	 */
	public Object getBean(String name, Object[] args) throws BeansException {
		return getBean(name, null, args);
	}

	/**
	 * 根据指定的名称，返回一个bean的实例，可能是共享的或者独立的。<br/>
	 * Return an instance, which may be shared or independent, of the specified
	 * bean.
	 * 
	 * @param name
	 *            要获取的bean的名称<br/>
	 *            the name of the bean to retrieve
	 * @param requiredType
	 *            要获取的bean的类型<br/>
	 *            the required type of the bean to retrieve
	 * @param args
	 *            arguments to use if creating a prototype using explicit
	 *            arguments to a static factory method. It is invalid to use a
	 *            non-null args value in any other case.
	 * @return an instance of the bean
	 * @throws BeansException
	 *             if the bean could not be created
	 */
	public Object getBean(String name, Class requiredType, final Object[] args)
			throws BeansException {
		final String beanName = transformedBeanName(name);
		Object bean = null;

		// 首先验证缓存中
		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null) {
			if (logger.isDebugEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.debug("Returning eagerly cached instance of singleton bean '"
							+ beanName
							+ "' that is not fully initialized yet - a consequence of a circular reference");
				} else {
					logger.debug("Returning cached instance of singleton bean '"
							+ beanName + "'");
				}
			}
			if (containsBeanDefinition(beanName)) {
				RootBeanDefinition mbd = getMergedBeanDefinition(beanName,
						false);
				bean = getObjectForBeanInstance(sharedInstance, name, mbd);
			} else {
				bean = getObjectForBeanInstance(sharedInstance, name, null);
			}
		}

		else {
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Check if bean definition exists in this factory.
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					// Delegation to parent with args only possible for
					// AbstractBeanFactory.
					return ((AbstractBeanFactory) parentBeanFactory).getBean(
							nameToLookup, requiredType, args);
				} else if (args == null) {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory
							.getBean(nameToLookup, requiredType);
				} else {
					throw new NoSuchBeanDefinitionException(
							beanName,
							"Cannot delegate to parent BeanFactory because it does not supported passed-in arguments");
				}
			}

			this.alreadyCreated.add(beanName);

			final RootBeanDefinition mbd = getMergedBeanDefinition(beanName,
					false);
			checkMergedBeanDefinition(mbd, beanName, args);

			// Create bean instance.
			if (mbd.isSingleton()) {
				sharedInstance = getSingleton(beanName, new ObjectFactory() {
					public Object getObject() throws BeansException {
						try {
							return createBean(beanName, mbd, args);
						} catch (BeansException ex) {
							// Explicitly remove instance from singleton cache:
							// It might have been put there
							// eagerly by the creation process, to allow for
							// circular reference resolution.
							// Also remove any beans that received a temporary
							// reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					}
				});
				bean = getObjectForBeanInstance(sharedInstance, name, mbd);
			}

			else if (mbd.isPrototype()) {
				// It's a prototype -> create a new instance.
				Object prototypeInstance = null;
				try {
					beforePrototypeCreation(beanName);
					prototypeInstance = createBean(beanName, mbd, args);
				} finally {
					afterPrototypeCreation(beanName);
				}
				bean = getObjectForBeanInstance(prototypeInstance, name, mbd);
			}

			else {
				String scopeName = mbd.getScope();
				final Scope scope = (Scope) this.scopes.get(scopeName);
				if (scope == null) {
					throw new IllegalStateException(
							"No Scope registered for scope '" + scopeName + "'");
				}
				try {
					Object scopedInstance = scope.get(beanName,
							new ObjectFactory() {
								public Object getObject() throws BeansException {
									beforePrototypeCreation(beanName);
									try {
										Object bean = createBean(beanName, mbd,
												args);
										if (requiresDestruction(bean, mbd)) {
											scope.registerDestructionCallback(
													beanName,
													new DisposableBeanAdapter(
															bean,
															beanName,
															mbd,
															getBeanPostProcessors()));
										}
										return bean;
									} finally {
										afterPrototypeCreation(beanName);
									}
								}
							});
					bean = getObjectForBeanInstance(scopedInstance, name, mbd);
				} catch (IllegalStateException ex) {
					throw new BeanCreationException(
							beanName,
							"Scope '"
									+ scopeName
									+ "' is not active for the current thread; "
									+ "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
							ex);
				}
			}
		}

		// Check if required type matches the type of the actual bean instance.
		if (requiredType != null && bean != null
				&& !requiredType.isAssignableFrom(bean.getClass())) {
			throw new BeanNotOfRequiredTypeException(name, requiredType,
					bean.getClass());
		}
		return bean;
	}

	public boolean containsBean(String name) {
		String beanName = transformedBeanName(name);
		if (containsSingleton(beanName) || containsBeanDefinition(beanName)) {
			return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(name));
		}
		// Not found -> check parent.
		BeanFactory parentBeanFactory = getParentBeanFactory();
		return (parentBeanFactory != null && parentBeanFactory
				.containsBean(originalBeanName(name)));
	}

	public boolean isSingleton(String name)
			throws NoSuchBeanDefinitionException {
		String beanName = transformedBeanName(name);

		Object beanInstance = getSingleton(beanName);
		if (beanInstance != null) {
			if (beanInstance instanceof FactoryBean) {
				return (BeanFactoryUtils.isFactoryDereference(name) || ((FactoryBean) beanInstance)
						.isSingleton());
			} else {
				return !BeanFactoryUtils.isFactoryDereference(name);
			}
		}

		else {
			// No singleton instance found -> check bean definition.
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// No bean definition found in this factory -> delegate to
				// parent.
				return parentBeanFactory.isSingleton(originalBeanName(name));
			}

			RootBeanDefinition bd = getMergedBeanDefinition(beanName, false);

			// In case of FactoryBean, return singleton status of created object
			// if not a dereference.
			if (bd.isSingleton()) {
				if (isBeanClassMatch(beanName, bd, FactoryBean.class)) {
					if (BeanFactoryUtils.isFactoryDereference(name)) {
						return true;
					}
					FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX
							+ beanName);
					return factoryBean.isSingleton();
				} else {
					return !BeanFactoryUtils.isFactoryDereference(name);
				}
			} else {
				return false;
			}
		}
	}

	public boolean isPrototype(String name)
			throws NoSuchBeanDefinitionException {
		String beanName = transformedBeanName(name);

		BeanFactory parentBeanFactory = getParentBeanFactory();
		if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
			// No bean definition found in this factory -> delegate to parent.
			return parentBeanFactory.isPrototype(originalBeanName(name));
		}

		RootBeanDefinition bd = getMergedBeanDefinition(beanName, false);

		// In case of FactoryBean, return singleton status of created object if
		// not a dereference.
		if (bd.isPrototype()) {
			return (!BeanFactoryUtils.isFactoryDereference(name) || isBeanClassMatch(
					beanName, bd, FactoryBean.class));
		} else {
			// Singleton or scoped - not a prototype.
			// However, FactoryBean may still produce a prototype object...
			if (!BeanFactoryUtils.isFactoryDereference(name)
					&& isBeanClassMatch(beanName, bd, FactoryBean.class)) {
				FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX
						+ beanName);
				return ((factoryBean instanceof SmartFactoryBean && ((SmartFactoryBean) factoryBean)
						.isPrototype()) || !factoryBean.isSingleton());
			} else {
				return false;
			}
		}
	}

	public boolean isTypeMatch(String name, Class targetType)
			throws NoSuchBeanDefinitionException {
		String beanName = transformedBeanName(name);
		Class typeToMatch = (targetType != null ? targetType : Object.class);

		// Check manually registered singletons.
		Object beanInstance = getSingleton(beanName);
		if (beanInstance != null) {
			if (beanInstance instanceof FactoryBean) {
				if (!BeanFactoryUtils.isFactoryDereference(name)) {
					Class type = getTypeForFactoryBean((FactoryBean) beanInstance);
					return (type != null && typeToMatch.isAssignableFrom(type));
				} else {
					return typeToMatch
							.isAssignableFrom(beanInstance.getClass());
				}
			} else {
				return !BeanFactoryUtils.isFactoryDereference(name)
						&& typeToMatch
								.isAssignableFrom(beanInstance.getClass());
			}
		}

		else {
			// No singleton instance found -> check bean definition.
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// No bean definition found in this factory -> delegate to
				// parent.
				return parentBeanFactory.isTypeMatch(originalBeanName(name),
						targetType);
			}

			RootBeanDefinition mbd = getMergedBeanDefinition(beanName, false);
			Class beanClass = predictBeanType(beanName, mbd);

			if (beanClass == null) {
				return false;
			}

			// Check bean class whether we're dealing with a FactoryBean.
			if (FactoryBean.class.isAssignableFrom(beanClass)) {
				if (!BeanFactoryUtils.isFactoryDereference(name)) {
					// If it's a FactoryBean, we want to look at what it
					// creates, not the factory class.
					Class type = getTypeForFactoryBean(beanName, mbd);
					return (type != null && typeToMatch.isAssignableFrom(type));
				} else {
					return typeToMatch.isAssignableFrom(beanClass);
				}
			} else {
				return !BeanFactoryUtils.isFactoryDereference(name)
						&& typeToMatch.isAssignableFrom(beanClass);
			}
		}
	}

	public Class getType(String name) throws NoSuchBeanDefinitionException {
		String beanName = transformedBeanName(name);

		// Check manually registered singletons.
		Object beanInstance = getSingleton(beanName);
		if (beanInstance != null) {
			if (beanInstance instanceof FactoryBean
					&& !BeanFactoryUtils.isFactoryDereference(name)) {
				return getTypeForFactoryBean((FactoryBean) beanInstance);
			} else {
				return beanInstance.getClass();
			}
		}

		else {
			// No singleton instance found -> check bean definition.
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// No bean definition found in this factory -> delegate to
				// parent.
				return parentBeanFactory.getType(originalBeanName(name));
			}

			RootBeanDefinition mbd = getMergedBeanDefinition(beanName, false);
			Class beanClass = predictBeanType(beanName, mbd);

			// Check bean class whether we're dealing with a FactoryBean.
			if (beanClass != null
					&& FactoryBean.class.isAssignableFrom(beanClass)) {
				if (!BeanFactoryUtils.isFactoryDereference(name)) {
					// If it's a FactoryBean, we want to look at what it
					// creates, not the factory class.
					return getTypeForFactoryBean(beanName, mbd);
				} else {
					return beanClass;
				}
			} else {
				return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass
						: null);
			}
		}
	}

	public String[] getAliases(String name) {
		String beanName = transformedBeanName(name);
		List aliases = new ArrayList();
		boolean factoryPrefix = name.startsWith(FACTORY_BEAN_PREFIX);
		String fullBeanName = beanName;
		if (factoryPrefix) {
			fullBeanName = FACTORY_BEAN_PREFIX + beanName;
		}
		if (!fullBeanName.equals(name)) {
			aliases.add(fullBeanName);
		}
		synchronized (this.aliasMap) {
			for (Iterator it = this.aliasMap.entrySet().iterator(); it
					.hasNext();) {
				Map.Entry entry = (Map.Entry) it.next();
				if (entry.getValue().equals(beanName)) {
					String key = (factoryPrefix ? FACTORY_BEAN_PREFIX : "")
							+ entry.getKey();
					if (!key.equals(name)) {
						aliases.add(key);
					}
				}
			}
		}
		if (!containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null) {
				aliases.addAll(Arrays.asList(parentBeanFactory
						.getAliases(fullBeanName)));
			}
		}
		return StringUtils.toStringArray(aliases);
	}

	// ---------------------------------------------------------------------
	// Implementation of HierarchicalBeanFactory interface
	// ---------------------------------------------------------------------

	public BeanFactory getParentBeanFactory() {
		return this.parentBeanFactory;
	}

	public boolean containsLocalBean(String name) {
		String beanName = transformedBeanName(name);
		return ((containsSingleton(beanName) || containsBeanDefinition(beanName)) && (!BeanFactoryUtils
				.isFactoryDereference(name) || isFactoryBean(beanName)));
	}

	// ---------------------------------------------------------------------
	// Implementation of ConfigurableBeanFactory interface
	// ---------------------------------------------------------------------

	public void setParentBeanFactory(BeanFactory parentBeanFactory) {
		if (this.parentBeanFactory != null
				&& this.parentBeanFactory != parentBeanFactory) {
			throw new IllegalStateException(
					"Already associated with parent BeanFactory: "
							+ this.parentBeanFactory);
		}
		this.parentBeanFactory = parentBeanFactory;
	}

	public void setBeanClassLoader(ClassLoader beanClassLoader) {
		this.beanClassLoader = (beanClassLoader != null ? beanClassLoader
				: ClassUtils.getDefaultClassLoader());
	}

	public ClassLoader getBeanClassLoader() {
		return this.beanClassLoader;
	}

	public void setCacheBeanMetadata(boolean cacheBeanMetadata) {
		this.cacheBeanMetadata = cacheBeanMetadata;
	}

	public boolean isCacheBeanMetadata() {
		return this.cacheBeanMetadata;
	}

	public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
		Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
		this.propertyEditorRegistrars.add(registrar);
	}

	/**
	 * Return the set of PropertyEditorRegistrars.
	 */
	public Set getPropertyEditorRegistrars() {
		return this.propertyEditorRegistrars;
	}

	public void registerCustomEditor(Class requiredType,
			PropertyEditor propertyEditor) {
		Assert.notNull(requiredType, "Required type must not be null");
		Assert.notNull(propertyEditor, "PropertyEditor must not be null");
		this.customEditors.put(requiredType, propertyEditor);
	}

	/**
	 * Return the map of custom editors, with Classes as keys and
	 * PropertyEditors as values.
	 */
	public Map getCustomEditors() {
		return this.customEditors;
	}

	public TypeConverter getTypeConverter() {
		SimpleTypeConverter typeConverter = new SimpleTypeConverter();
		registerCustomEditors(typeConverter);
		return typeConverter;
	}

	public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
		Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
		this.beanPostProcessors.add(beanPostProcessor);
		if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
			this.hasInstantiationAwareBeanPostProcessors = true;
		}
		if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
			this.hasDestructionAwareBeanPostProcessors = true;
		}
	}

	public int getBeanPostProcessorCount() {
		return this.beanPostProcessors.size();
	}

	/**
	 * Return the list of BeanPostProcessors that will get applied to beans
	 * created with this factory.
	 */
	public List getBeanPostProcessors() {
		return this.beanPostProcessors;
	}

	/**
	 * Return whether this factory holds a InstantiationAwareBeanPostProcessor
	 * that will get applied to singleton beans on shutdown.
	 * 
	 * @see #addBeanPostProcessor
	 * @see org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor
	 */
	protected boolean hasInstantiationAwareBeanPostProcessors() {
		return this.hasInstantiationAwareBeanPostProcessors;
	}

	/**
	 * Return whether this factory holds a DestructionAwareBeanPostProcessor
	 * that will get applied to singleton beans on shutdown.
	 * 
	 * @see #addBeanPostProcessor
	 * @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
	 */
	protected boolean hasDestructionAwareBeanPostProcessors() {
		return this.hasDestructionAwareBeanPostProcessors;
	}

	public void registerScope(String scopeName, Scope scope) {
		Assert.notNull(scopeName, "Scope identifier must not be null");
		Assert.notNull(scope, "Scope must not be null");
		if (SCOPE_SINGLETON.equals(scopeName)
				|| SCOPE_PROTOTYPE.equals(scopeName)) {
			throw new IllegalArgumentException(
					"Cannot replace existing scopes 'singleton' and 'prototype'");
		}
		this.scopes.put(scopeName, scope);
	}

	public String[] getRegisteredScopeNames() {
		return StringUtils.toStringArray(this.scopes.keySet());
	}

	public Scope getRegisteredScope(String scopeName) {
		Assert.notNull(scopeName, "Scope identifier must not be null");
		return (Scope) this.scopes.get(scopeName);
	}

	public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
		Assert.notNull(otherFactory, "BeanFactory must not be null");
		setBeanClassLoader(otherFactory.getBeanClassLoader());
		setCacheBeanMetadata(otherFactory.isCacheBeanMetadata());
		if (otherFactory instanceof AbstractBeanFactory) {
			AbstractBeanFactory otherAbstractFactory = (AbstractBeanFactory) otherFactory;
			this.customEditors.putAll(otherAbstractFactory.customEditors);
			this.propertyEditorRegistrars
					.addAll(otherAbstractFactory.propertyEditorRegistrars);
			this.beanPostProcessors
					.addAll(otherAbstractFactory.beanPostProcessors);
			this.hasInstantiationAwareBeanPostProcessors = this.hasInstantiationAwareBeanPostProcessors
					|| otherAbstractFactory.hasInstantiationAwareBeanPostProcessors;
			this.hasDestructionAwareBeanPostProcessors = this.hasDestructionAwareBeanPostProcessors
					|| otherAbstractFactory.hasDestructionAwareBeanPostProcessors;
			this.scopes.putAll(otherAbstractFactory.scopes);
		}
	}

	public void registerAlias(String beanName, String alias)
			throws BeanDefinitionStoreException {
		Assert.hasText(beanName, "'beanName' must not be empty");
		Assert.hasText(alias, "'alias' must not be empty");
		if (!alias.equals(beanName)) {
			// Only actually register the alias if it is not equal to the bean
			// name itself.
			if (logger.isDebugEnabled()) {
				logger.debug("Registering alias '" + alias
						+ "' for bean with name '" + beanName + "'");
			}
			synchronized (this.aliasMap) {
				Object registeredName = this.aliasMap.get(alias);
				if (registeredName != null && !registeredName.equals(beanName)) {
					throw new BeanDefinitionStoreException(
							"Cannot register alias '"
									+ alias
									+ "' for bean name '"
									+ beanName
									+ "': It's already registered for bean name '"
									+ registeredName + "'.");
				}
				this.aliasMap.put(alias, beanName);
			}
		}
	}

	/**
	 * Callback before prototype creation.
	 * <p>
	 * The default implementation register the prototype as currently in
	 * creation.
	 * 
	 * @param beanName
	 *            the name of the prototype about to be created
	 * @see #isPrototypeCurrentlyInCreation
	 */
	protected void beforePrototypeCreation(String beanName) {
		Object curVal = this.prototypesCurrentlyInCreation.get();
		if (curVal == null) {
			this.prototypesCurrentlyInCreation.set(beanName);
		} else if (curVal instanceof String) {
			Set beanNameSet = new HashSet(2);
			beanNameSet.add(curVal);
			beanNameSet.add(beanName);
			this.prototypesCurrentlyInCreation.set(beanNameSet);
		} else {
			Set beanNameSet = (Set) curVal;
			beanNameSet.add(beanName);
		}
	}

	/**
	 * Callback after prototype creation.
	 * <p>
	 * The default implementation marks the prototype as not in creation
	 * anymore.
	 * 
	 * @param beanName
	 *            the name of the prototype that has been created
	 * @see #isPrototypeCurrentlyInCreation
	 */
	protected void afterPrototypeCreation(String beanName) {
		Object curVal = this.prototypesCurrentlyInCreation.get();
		if (curVal instanceof String) {
			this.prototypesCurrentlyInCreation.set(null);
		} else if (curVal instanceof Set) {
			Set beanNameSet = (Set) curVal;
			beanNameSet.remove(beanName);
			if (beanNameSet.isEmpty()) {
				this.prototypesCurrentlyInCreation.set(null);
			}
		}
	}

	/**
	 * Return whether the specified prototype bean is currently in creation
	 * (within the current thread).
	 * 
	 * @param beanName
	 *            the name of the bean
	 */
	protected final boolean isPrototypeCurrentlyInCreation(String beanName) {
		Object curVal = this.prototypesCurrentlyInCreation.get();
		return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set) curVal)
				.contains(beanName))));
	}

	public boolean isCurrentlyInCreation(String beanName) {
		return isSingletonCurrentlyInCreation(beanName)
				|| isPrototypeCurrentlyInCreation(beanName);
	}

	public void destroyBean(String beanName, Object beanInstance) {
		destroyBean(beanName, beanInstance, getMergedBeanDefinition(beanName));
	}

	/**
	 * Destroy the given bean instance (usually a prototype instance obtained
	 * from this factory) according to the given bean definition.
	 * 
	 * @param beanName
	 *            the name of the bean definition
	 * @param beanInstance
	 *            the bean instance to destroy
	 * @param mbd
	 *            the merged bean definition
	 */
	protected void destroyBean(String beanName, Object beanInstance,
			RootBeanDefinition mbd) {
		new DisposableBeanAdapter(beanInstance, beanName, mbd,
				getBeanPostProcessors()).destroy();
	}

	public void destroyScopedBean(String beanName) {
		RootBeanDefinition mbd = getMergedBeanDefinition(beanName);
		if (mbd.isSingleton() || mbd.isPrototype()) {
			throw new IllegalArgumentException("Bean name '" + beanName
					+ "' does not correspond to an object in a Scope");
		}
		String scopeName = mbd.getScope();
		Scope scope = (Scope) this.scopes.get(scopeName);
		if (scope == null) {
			throw new IllegalStateException("No Scope registered for scope '"
					+ scopeName + "'");
		}
		Object bean = scope.remove(beanName);
		if (bean != null) {
			destroyBean(beanName, bean, mbd);
		}
	}

	// ---------------------------------------------------------------------
	// Implementation methods
	// ---------------------------------------------------------------------

	/**
	 * 返回bean的名称，如果需要的话，取出factory定义的前缀，将别名转化为正式名称。<br/>
	 * Return the bean name, stripping out the factory dereference prefix if
	 * necessary, and resolving aliases to canonical names.
	 * 
	 * @param name
	 *            the user-specified name
	 * @return the transformed bean name
	 */
	protected String transformedBeanName(String name) {
		String canonicalName = BeanFactoryUtils.transformedBeanName(name);
		// Handle aliasing.
		String resolvedName = null;
		do {
			resolvedName = (String) this.aliasMap.get(canonicalName);
			if (resolvedName != null) {
				canonicalName = resolvedName;
			}
		} while (resolvedName != null);
		return canonicalName;
	}

	/**
	 * Determine the original bean name, resolving locally defined aliases to
	 * canonical names.
	 * 
	 * @param name
	 *            the user-specified name
	 * @return the original bean name
	 */
	protected String originalBeanName(String name) {
		String beanName = transformedBeanName(name);
		if (name.startsWith(FACTORY_BEAN_PREFIX)) {
			beanName = FACTORY_BEAN_PREFIX + beanName;
		}
		return beanName;
	}

	/**
	 * Determine whether this given bean name is defines as an alias (as opposed
	 * to the name of an actual bean definition).
	 * 
	 * @param beanName
	 *            the bean name to check
	 * @return whether the given name is an alias
	 */
	protected boolean isAlias(String beanName) {
		return this.aliasMap.containsKey(beanName);
	}

	/**
	 * Initialize the given BeanWrapper with the custom editors registered with
	 * this factory. To be called for BeanWrappers that will create and populate
	 * bean instances.
	 * <p>
	 * The default implementation delegates to
	 * <code>registerCustomEditors</code>. Can be overridden in subclasses.
	 * 
	 * @param bw
	 *            the BeanWrapper to initialize
	 * @see #registerCustomEditors
	 */
	protected void initBeanWrapper(BeanWrapper bw) {
		registerCustomEditors(bw);
	}

	/**
	 * Initialize the given PropertyEditorRegistry with the custom editors
	 * registered with this BeanFactory.
	 * <p>
	 * To be called for BeanWrappers that will create and populate bean
	 * instances, and for SimpleTypeConverter used for constructor argument and
	 * factory method type conversion.
	 * 
	 * @param registry
	 *            the PropertyEditorRegistry to initialize
	 */
	protected void registerCustomEditors(PropertyEditorRegistry registry) {
		PropertyEditorRegistrySupport registrySupport = (registry instanceof PropertyEditorRegistrySupport ? (PropertyEditorRegistrySupport) registry
				: null);
		if (registrySupport != null) {
			registrySupport.useConfigValueEditors();
		}
		for (Iterator it = this.propertyEditorRegistrars.iterator(); it
				.hasNext();) {
			PropertyEditorRegistrar registrar = (PropertyEditorRegistrar) it
					.next();
			registrar.registerCustomEditors(registry);
		}
		for (Iterator it = this.customEditors.entrySet().iterator(); it
				.hasNext();) {
			Map.Entry entry = (Map.Entry) it.next();
			Class clazz = (Class) entry.getKey();
			PropertyEditor editor = (PropertyEditor) entry.getValue();
			// Register the editor as shared instance, if possible,
			// to make it clear that it might be used concurrently.
			if (registrySupport != null) {
				registrySupport.registerSharedEditor(clazz, editor);
			} else {
				registry.registerCustomEditor(clazz, editor);
			}
		}
	}

	/**
	 * Return a RootBeanDefinition for the given bean name, merging a child bean
	 * definition with its parent if necessary.
	 * 
	 * @param beanName
	 *            the name of the bean to retrieve the merged definition for
	 * @return a (potentially merged) RootBeanDefinition for the given bean
	 * @throws NoSuchBeanDefinitionException
	 *             if there is no bean with the given name
	 * @throws BeanDefinitionStoreException
	 *             in case of an invalid bean definition
	 */
	public RootBeanDefinition getMergedBeanDefinition(String beanName)
			throws BeansException {
		return getMergedBeanDefinition(beanName, false);
	}

	/**
	 * Return a RootBeanDefinition, even by traversing parent if the parameter
	 * is a child definition. Can ask the parent bean factory if not found in
	 * this instance.
	 * 
	 * @param beanName
	 *            the name of the bean to retrieve the merged definition for
	 * @param includingAncestors
	 *            whether to ask the parent bean factory if not found in this
	 *            instance
	 * @return a (potentially merged) RootBeanDefinition for the given bean
	 * @throws NoSuchBeanDefinitionException
	 *             if there is no bean with the given name
	 * @throws BeanDefinitionStoreException
	 *             in case of an invalid bean definition
	 */
	protected RootBeanDefinition getMergedBeanDefinition(String beanName,
			boolean includingAncestors) throws BeansException {

		// Efficiently check whether bean definition exists in this factory.
		if (includingAncestors && !containsBeanDefinition(beanName)
				&& getParentBeanFactory() instanceof AbstractBeanFactory) {
			return ((AbstractBeanFactory) getParentBeanFactory())
					.getMergedBeanDefinition(beanName, true);
		}

		// Resolve merged bean definition locally.
		return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
	}

	/**
	 * Return a RootBeanDefinition for the given top-level bean, by merging with
	 * the parent if the given bean's definition is a child bean definition.
	 * 
	 * @param beanName
	 *            the name of the bean definition
	 * @param bd
	 *            the original bean definition (Root/ChildBeanDefinition)
	 * @return a (potentially merged) RootBeanDefinition for the given bean
	 * @throws BeanDefinitionStoreException
	 *             in case of an invalid bean definition
	 */
	protected RootBeanDefinition getMergedBeanDefinition(String beanName,
			BeanDefinition bd) throws BeanDefinitionStoreException {

		return getMergedBeanDefinition(beanName, bd, null);
	}

	/**
	 * Return a RootBeanDefinition for the given bean, by merging with the
	 * parent if the given bean's definition is a child bean definition.
	 * 
	 * @param beanName
	 *            the name of the bean definition
	 * @param bd
	 *            the original bean definition (Root/ChildBeanDefinition)
	 * @param containingBd
	 *            the containing bean definition in case of inner bean, or
	 *            <code>null</code> in case of a top-level bean
	 * @return a (potentially merged) RootBeanDefinition for the given bean
	 * @throws BeanDefinitionStoreException
	 *             in case of an invalid bean definition
	 */
	protected RootBeanDefinition getMergedBeanDefinition(String beanName,
			BeanDefinition bd, BeanDefinition containingBd)
			throws BeanDefinitionStoreException {

		RootBeanDefinition mbd = null;
		// Quick check on the concurrent map first, with minimal locking.
		if (containingBd == null) {
			mbd = (RootBeanDefinition) this.mergedBeanDefinitions.get(beanName);
		}
		if (mbd == null) {

			synchronized (this.mergedBeanDefinitions) {
				// Second check with full lock now, to enforce the same merged
				// instance.
				if (containingBd == null) {
					mbd = (RootBeanDefinition) this.mergedBeanDefinitions
							.get(beanName);
				}
				if (mbd == null) {

					if (bd instanceof RootBeanDefinition) {
						// Use copy of given root bean definition.
						mbd = new RootBeanDefinition((RootBeanDefinition) bd);
					}

					else if (bd instanceof ChildBeanDefinition) {
						// Child bean definition: needs to be merged with
						// parent.
						ChildBeanDefinition cbd = (ChildBeanDefinition) bd;
						RootBeanDefinition pbd = null;
						try {
							String parentBeanName = transformedBeanName(cbd
									.getParentName());
							if (!beanName.equals(parentBeanName)) {
								pbd = getMergedBeanDefinition(parentBeanName,
										true);
							} else {
								if (getParentBeanFactory() instanceof AbstractBeanFactory) {
									AbstractBeanFactory parentFactory = (AbstractBeanFactory) getParentBeanFactory();
									pbd = parentFactory
											.getMergedBeanDefinition(
													parentBeanName, true);
								} else {
									throw new NoSuchBeanDefinitionException(
											cbd.getParentName(),
											"Parent name '"
													+ cbd.getParentName()
													+ "' is equal to bean name '"
													+ beanName
													+ "': cannot be resolved without an AbstractBeanFactory parent");
								}
							}
						} catch (NoSuchBeanDefinitionException ex) {
							throw new BeanDefinitionStoreException(
									cbd.getResourceDescription(), beanName,
									"Could not resolve parent bean definition '"
											+ cbd.getParentName() + "'", ex);
						}

						// Deep copy with overridden values.
						mbd = new RootBeanDefinition(pbd);
						mbd.overrideFrom(cbd);
					}

					else {
						throw new BeanDefinitionStoreException(
								bd.getResourceDescription(), beanName,
								"Definition is neither a RootBeanDefinition nor a ChildBeanDefinition: "
										+ bd);
					}

					// A bean contained in a non-singleton bean cannot be a
					// singleton itself.
					// Let's correct this on the fly here, since this might be
					// the result of
					// parent-child merging for the outer bean, in which case
					// the original inner bean
					// definition will not have inherited the merged outer
					// bean's singleton status.
					if (containingBd != null && !containingBd.isSingleton()
							&& mbd.isSingleton()) {
						mbd.setSingleton(false);
					}

					// Only cache the merged bean definition if we're already
					// about to create an
					// instance of the bean, or at least have already created an
					// instance before.
					if (containingBd == null && isCacheBeanMetadata()
							&& this.alreadyCreated.contains(beanName)) {
						this.mergedBeanDefinitions.put(beanName, mbd);
					}
				}
			}
		}

		return mbd;
	}

	/**
	 * Check the given merged bean definition, potentially throwing validation
	 * exceptions.
	 * 
	 * @param mbd
	 *            the merged bean definition to check
	 * @param beanName
	 *            the name of the bean
	 * @param args
	 *            the arguments for bean creation, if any
	 * @throws BeanDefinitionStoreException
	 *             in case of validation failure
	 */
	protected void checkMergedBeanDefinition(RootBeanDefinition mbd,
			String beanName, Object[] args) throws BeanDefinitionStoreException {

		// check if bean definition is not abstract
		if (mbd.isAbstract()) {
			throw new BeanIsAbstractException(beanName);
		}

		// Check validity of the usage of the args parameter. This can
		// only be used for prototypes constructed via a factory method.
		if (args != null) {
			if (mbd.isSingleton()) {
				throw new BeanDefinitionStoreException(
						"Cannot specify arguments in the getBean() method when referring to a singleton bean definition");
			} else if (mbd.getFactoryMethodName() == null) {
				throw new BeanDefinitionStoreException(
						"Can only specify arguments in the getBean() method in conjunction with a factory method");
			}
		}
	}

	/**
	 * Remove the merged bean definition for the specified bean, recreating it
	 * on next access.
	 * 
	 * @param beanName
	 *            the bean name to clear the merged definition for
	 */
	protected void clearMergedBeanDefinition(String beanName) {
		this.mergedBeanDefinitions.remove(beanName);
	}

	/**
	 * Resolve the bean class for the specified bean definition, resolving a
	 * bean class name into a Class reference (if necessary) and storing the
	 * resolved Class in the bean definition for further use.
	 * 
	 * @param mbd
	 *            the merged bean definition to determine the class for
	 * @param beanName
	 *            the name of the bean (for error handling purposes)
	 * @return the resolved bean class (or <code>null</code> if none)
	 * @throws CannotLoadBeanClassException
	 *             if we failed to load the class
	 */
	protected Class resolveBeanClass(RootBeanDefinition mbd, String beanName)
			throws CannotLoadBeanClassException {
		if (mbd.hasBeanClass()) {
			return mbd.getBeanClass();
		}
		try {
			return mbd.resolveBeanClass(getBeanClassLoader());
		} catch (ClassNotFoundException ex) {
			throw new CannotLoadBeanClassException(
					mbd.getResourceDescription(), beanName,
					mbd.getBeanClassName(), ex);
		} catch (LinkageError err) {
			throw new CannotLoadBeanClassException(
					mbd.getResourceDescription(), beanName,
					mbd.getBeanClassName(), err);
		}
	}

	/**
	 * Check whether the bean class of the given bean definition matches the
	 * specified target type. Allows for lazy loading of the actual bean class,
	 * provided that the type match can be determined otherwise.
	 * <p>
	 * The default implementation simply delegates to the standard
	 * <code>resolveBeanClass</code> method. Subclasses may override this to use
	 * a different strategy, such as a throwaway class loaer.
	 * 
	 * @param beanName
	 *            the name of the bean (for error handling purposes)
	 * @param mbd
	 *            the merged bean definition to determine the class for
	 * @param targetType
	 *            the type to match against (never <code>null</code>)
	 * @return the resolved bean class (or <code>null</code> if none)
	 * @throws CannotLoadBeanClassException
	 *             if we failed to load the class
	 * @see #resolveBeanClass
	 */
	protected boolean isBeanClassMatch(String beanName, RootBeanDefinition mbd,
			Class targetType) throws CannotLoadBeanClassException {

		Class beanClass = resolveBeanClass(mbd, beanName);
		return (beanClass != null && targetType.isAssignableFrom(beanClass));
	}

	/**
	 * Predict the eventual bean type (of the processed bean instance) for the
	 * specified bean. Called by {@link #getType} and {@link #isTypeMatch}. Does
	 * not need to handle FactoryBeans specifically, since it is only supposed
	 * to operate on the raw bean type.
	 * <p>
	 * This implementation is simplistic in that it is not able to handle
	 * factory methods and InstantiationAwareBeanPostProcessors. It only
	 * predicts the bean type correctly for a standard bean. To be overridden in
	 * subclasses, applying more sophisticated type detection.
	 * 
	 * @param beanName
	 *            the name of the bean
	 * @param mbd
	 *            the merged bean definition to determine the type for
	 * @return the type of the bean, or <code>null</code> if not predictable
	 */
	protected Class predictBeanType(String beanName, RootBeanDefinition mbd) {
		if (mbd.getFactoryMethodName() != null) {
			return null;
		}
		return resolveBeanClass(mbd, beanName);
	}

	/**
	 * Determine the bean type for the given FactoryBean definition, as far as
	 * possible. Only called if there is no singleton instance registered for
	 * the target bean already.
	 * <p>
	 * The default implementation creates the FactoryBean via
	 * <code>getBean</code> to call its <code>getObjectType</code> method.
	 * Subclasses are encouraged to optimize this, typically by just
	 * instantiating the FactoryBean but not populating it yet, trying whether
	 * its <code>getObjectType</code> method already returns a type. If no type
	 * found, a full FactoryBean creation as performed by this implementation
	 * should be used as fallback.
	 * 
	 * @param beanName
	 *            the name of the bean
	 * @param mbd
	 *            the merged bean definition for the bean
	 * @return the type for the bean if determinable, or <code>null</code> else
	 * @see org.springframework.beans.factory.FactoryBean#getObjectType()
	 * @see #getBean(String)
	 */
	protected Class getTypeForFactoryBean(String beanName,
			RootBeanDefinition mbd) {
		if (!mbd.isSingleton()) {
			return null;
		}
		try {
			FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX
					+ beanName);
			return getTypeForFactoryBean(factoryBean);
		} catch (BeanCreationException ex) {
			// Can only happen when getting a FactoryBean.
			if (ex.contains(BeanCurrentlyInCreationException.class)) {
				logger.debug(
						"Ignoring bean creation exception on FactoryBean type check",
						ex);
			} else {
				String msg = "Encountered FactoryBean creation exception for bean '"
						+ beanName + "' - couldn't check type!";
				if (logger.isDebugEnabled()) {
					logger.debug(msg, ex);
				} else if (logger.isWarnEnabled()) {
					logger.warn(msg + " " + ex.toString());
				}
			}
			return null;
		}
	}

	/**
	 * Determine the type for the given FactoryBean.
	 * 
	 * @param factoryBean
	 *            the FactoryBean instance to check
	 * @return the FactoryBean's object type, or <code>null</code> if the type
	 *         cannot be determined yet
	 */
	protected Class getTypeForFactoryBean(FactoryBean factoryBean) {
		try {
			return factoryBean.getObjectType();
		} catch (Throwable ex) {
			// Thrown from the FactoryBean's getObjectType implementation.
			logger.warn(
					"FactoryBean threw exception from getObjectType, despite the contract saying "
							+ "that it should return null if the type of its object cannot be determined yet",
					ex);
			return null;
		}
	}

	/**
	 * Get the object for the given bean instance, either the bean instance
	 * itself or its created object in case of a FactoryBean.
	 * 
	 * @param beanInstance
	 *            the shared bean instance
	 * @param name
	 *            name that may include factory dereference prefix
	 * @param mbd
	 *            the merged bean definition
	 * @return the object to expose for the bean
	 */
	protected Object getObjectForBeanInstance(Object beanInstance, String name,
			RootBeanDefinition mbd) {
		// Don't let calling code try to dereference the
		// bean factory if the bean isn't a factory.
		if (BeanFactoryUtils.isFactoryDereference(name)
				&& !(beanInstance instanceof FactoryBean)) {
			throw new BeanIsNotAFactoryException(transformedBeanName(name),
					beanInstance.getClass());
		}

		boolean shared = (mbd == null || mbd.isSingleton());
		Object object = beanInstance;

		// Now we have the bean instance, which may be a normal bean or a
		// FactoryBean.
		// If it's a FactoryBean, we use it to create a bean instance, unless
		// the
		// caller actually wants a reference to the factory.
		if (beanInstance instanceof FactoryBean) {
			if (!BeanFactoryUtils.isFactoryDereference(name)) {
				// Return bean instance from factory.
				FactoryBean factory = (FactoryBean) beanInstance;
				String beanName = transformedBeanName(name);
				// Cache object obtained from FactoryBean if it is a singleton.
				if (shared && factory.isSingleton()) {
					synchronized (getSingletonMutex()) {
						object = this.factoryBeanObjectCache.get(beanName);
						if (object == null) {
							object = getObjectFromFactoryBean(factory,
									beanName, mbd);
							this.factoryBeanObjectCache.put(beanName, object);
						}
					}
				} else {
					object = getObjectFromFactoryBean(factory, beanName, mbd);
				}
			}
		}

		return object;
	}

	/**
	 * Obtain an object to expose from the given FactoryBean.
	 * 
	 * @param factory
	 *            the FactoryBean instance
	 * @param beanName
	 *            the name of the bean
	 * @param mbd
	 *            the merged bean definition
	 * @return the object obtained from the FactoryBean
	 * @throws BeanCreationException
	 *             if FactoryBean object creation failed
	 * @see org.springframework.beans.factory.FactoryBean#getObject()
	 */
	protected Object getObjectFromFactoryBean(FactoryBean factory,
			String beanName, RootBeanDefinition mbd)
			throws BeanCreationException {

		Object object;

		try {
			object = factory.getObject();
		} catch (FactoryBeanNotInitializedException ex) {
			throw new BeanCurrentlyInCreationException(beanName, ex.toString());
		} catch (Throwable ex) {
			throw new BeanCreationException(beanName,
					"FactoryBean threw exception on object creation", ex);
		}

		// Do not accept a null value for a FactoryBean that's not fully
		// initialized yet: Many FactoryBeans just return null then.
		if (object == null && isSingletonCurrentlyInCreation(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName,
					"FactoryBean which is currently in creation returned null from getObject");
		}

		if (object != null && (mbd == null || !mbd.isSynthetic())) {
			try {
				object = postProcessObjectFromFactoryBean(object, beanName);
			} catch (Throwable ex) {
				throw new BeanCreationException(mbd.getResourceDescription(),
						beanName,
						"Post-processing of the FactoryBean's object failed",
						ex);
			}
		}

		return object;
	}

	/**
	 * Post-process the given object that has been obtained from the
	 * FactoryBean. The resulting object will get exposed for bean references.
	 * <p>
	 * The default implementation simply returns the given object as-is.
	 * Subclasses may override this, for example, to apply post-processors.
	 * 
	 * @param object
	 *            the object obtained from the FactoryBean.
	 * @param beanName
	 *            the name of the bean
	 * @return the object to expose
	 * @throws BeansException
	 *             if any post-processing failed
	 */
	protected Object postProcessObjectFromFactoryBean(Object object,
			String beanName) throws BeansException {
		return object;
	}

	/**
	 * Determine whether the bean with the given name is a FactoryBean.
	 * 
	 * @param name
	 *            the name of the bean to check
	 * @return whether the bean is a FactoryBean (<code>false</code> means the
	 *         bean exists but is not a FactoryBean)
	 * @throws NoSuchBeanDefinitionException
	 *             if there is no bean with the given name
	 */
	public boolean isFactoryBean(String name)
			throws NoSuchBeanDefinitionException {
		String beanName = transformedBeanName(name);

		Object beanInstance = getSingleton(beanName);
		if (beanInstance != null) {
			return (beanInstance instanceof FactoryBean);
		}

		// No singleton instance found -> check bean definition.
		if (!containsBeanDefinition(beanName)
				&& getParentBeanFactory() instanceof AbstractBeanFactory) {
			// No bean definition found in this factory -> delegate to parent.
			return ((AbstractBeanFactory) getParentBeanFactory())
					.isFactoryBean(name);
		}

		RootBeanDefinition bd = getMergedBeanDefinition(beanName, false);
		return isBeanClassMatch(beanName, bd, FactoryBean.class);
	}

	/**
	 * Determine whether the given bean name is already in use within this
	 * factory, that is, whether there is a local bean registered under this
	 * name or an inner bean created with this name.
	 * 
	 * @param beanName
	 *            the name to check
	 */
	protected boolean isBeanNameInUse(String beanName) {
		return containsLocalBean(beanName) || hasDependentBean(beanName);
	}

	/**
	 * Determine whether the given bean requires destruction on shutdown.
	 * <p>
	 * The default implementation checks the DisposableBean interface as well as
	 * a specified destroy method and registered
	 * DestructionAwareBeanPostProcessors.
	 * 
	 * @param bean
	 *            the bean instance to check
	 * @param mbd
	 *            the corresponding bean definition
	 * @see org.springframework.beans.factory.DisposableBean
	 * @see AbstractBeanDefinition#getDestroyMethodName()
	 * @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
	 */
	protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
		return (bean instanceof DisposableBean
				|| mbd.getDestroyMethodName() != null || hasDestructionAwareBeanPostProcessors());
	}

	/**
	 * Add the given bean to the list of disposable beans in this factory,
	 * registering its DisposableBean interface and/or the given destroy method
	 * to be called on factory shutdown (if applicable). Only applies to
	 * singletons.
	 * <p>
	 * Also registers bean as dependent on other beans, according to the
	 * "depends-on" configuration in the bean definition.
	 * 
	 * @param beanName
	 *            the name of the bean
	 * @param bean
	 *            the bean instance
	 * @param mbd
	 *            the bean definition for the bean
	 * @see RootBeanDefinition#isSingleton
	 * @see RootBeanDefinition#getDependsOn
	 * @see #registerDisposableBean
	 * @see #registerDependentBean
	 */
	protected void registerDisposableBeanIfNecessary(String beanName,
			Object bean, RootBeanDefinition mbd) {
		if (mbd.isSingleton() && requiresDestruction(bean, mbd)) {
			// Register a DisposableBean implementation that performs all
			// destruction
			// work for the given bean: DestructionAwareBeanPostProcessors,
			// DisposableBean interface, custom destroy method.
			registerDisposableBean(beanName, new DisposableBeanAdapter(bean,
					beanName, mbd, getBeanPostProcessors()));

			// Register bean as dependent on other beans, if necessary,
			// for correct shutdown order.
			String[] dependsOn = mbd.getDependsOn();
			if (dependsOn != null) {
				for (int i = 0; i < dependsOn.length; i++) {
					registerDependentBean(dependsOn[i], beanName);
				}
			}
		}
	}

	/**
	 * Overridden to clear the FactoryBean object cache as well.
	 */
	protected void removeSingleton(String beanName) {
		super.removeSingleton(beanName);
		this.factoryBeanObjectCache.remove(beanName);
	}

	// ---------------------------------------------------------------------
	// Abstract methods to be implemented by subclasses
	// ---------------------------------------------------------------------

	/**
	 * Check if this bean factory contains a bean definition with the given
	 * name. Does not consider any hierarchy this factory may participate in.
	 * Invoked by <code>containsBean</code> when no cached singleton instance is
	 * found.
	 * <p>
	 * Depending on the nature of the concrete bean factory implementation, this
	 * operation might be expensive (for example, because of directory lookups
	 * in external registries). However, for listable bean factories, this
	 * usually just amounts to a local hash lookup: The operation is therefore
	 * part of the public interface there. The same implementation can serve for
	 * both this template method and the public interface method in that case.
	 * 
	 * @param beanName
	 *            the name of the bean to look for
	 * @return if this bean factory contains a bean definition with the given
	 *         name
	 * @see #containsBean
	 * @see org.springframework.beans.factory.ListableBeanFactory#containsBeanDefinition
	 */
	protected abstract boolean containsBeanDefinition(String beanName);

	/**
	 * Return the bean definition for the given bean name. Subclasses should
	 * normally implement caching, as this method is invoked by this class every
	 * time bean definition metadata is needed.
	 * <p>
	 * Depending on the nature of the concrete bean factory implementation, this
	 * operation might be expensive (for example, because of directory lookups
	 * in external registries). However, for listable bean factories, this
	 * usually just amounts to a local hash lookup: The operation is therefore
	 * part of the public interface there. The same implementation can serve for
	 * both this template method and the public interface method in that case.
	 * 
	 * @param beanName
	 *            the name of the bean to find a definition for
	 * @return the BeanDefinition for this prototype name (never
	 *         <code>null</code>)
	 * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
	 *             if the bean definition cannot be resolved
	 * @throws BeansException
	 *             in case of errors
	 * @see RootBeanDefinition
	 * @see ChildBeanDefinition
	 * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#getBeanDefinition
	 */
	protected abstract BeanDefinition getBeanDefinition(String beanName)
			throws BeansException;

	/**
	 * Create a bean instance for the given bean definition. The bean definition
	 * will already have been merged with the parent definition in case of a
	 * child definition.
	 * <p>
	 * All the other methods in this class invoke this method, although beans
	 * may be cached after being instantiated by this method. All bean
	 * instantiation within this class is performed by this method.
	 * 
	 * @param beanName
	 *            the name of the bean
	 * @param mbd
	 *            the merged bean definition for the bean
	 * @param args
	 *            arguments to use if creating a prototype using explicit
	 *            arguments to a static factory method. This parameter must be
	 *            <code>null</code> except in this case.
	 * @return a new instance of the bean
	 * @throws BeanCreationException
	 *             if the bean could not be created
	 */
	protected abstract Object createBean(String beanName,
			RootBeanDefinition mbd, Object[] args) throws BeanCreationException;

}
